package com.kpmg.datalake.common.dynamicdatasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.kpmg.datalake.common.utils.SpringUtil;
import com.kpmg.datalake.common.utils.signutils.Md5Util;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

/**
 * 适用用单机环境存储,如果非单机环境还要考虑 datasource对象非序列化问题。
 *
 * @author Alex.zhao [alex.zhao@kpmg.com] Created on azhao6 on 2018-06-22 14:47:16
 */
public abstract class AbstractDataSourceStoreServiceImpl implements DataSourceStoreService {

  private static final Logger logger = LoggerFactory
      .getLogger(AbstractDataSourceStoreServiceImpl.class);

  /**
   * 本地内存数据库
   */
  private static Map<Object, Object> dynamicMemoryMap = new HashMap<>(16);

  /**
   * 校验数据库是否存在
   *
   * @param dataSourceId 数据库唯一性标识
   * @return 是否存在
   */
  @Override
  public Boolean checkAndRefreshDataSource(String dataSourceId) throws SQLException {
    return checkAndRefreshDataSource(dataSourceId, null);
  }

  @Override
  public Boolean checkAndRefreshDataSource(String dataSourceId, DataSource dataSource)
      throws SQLException {
    Boolean dataSourceExist = dynamicMemoryMap.containsKey(dataSourceId);
    //先判断内存中是否存在，不存在判断共享存储，如果共享存储存在存在则初始化连接池
    if (!dataSourceExist) {
      dataSourceExist = this.containShardDataSourceId(dataSourceId);
      if (dataSourceExist || dataSource != null) {
        if (dataSource == null) {
          DatabaseDTO databaseDTO = this.getShardDataSource(dataSourceId);
          dataSource = this.databaseToDatasource(databaseDTO);
        }
        this.refreshTargetDataSource(dataSourceId, dataSource);
        dataSourceExist = true;
      }
    } else {
      dataSourceExist = true;
    }
    return dataSourceExist;
  }

  @Override
  public void refreshTargetDataSource(String dataSourceId, DataSource dataSource)
      throws SQLException {
    //刷新共享数据源
    this.setShardDataSource(dataSourceId, this.dataSourceToDatabase(dataSource));
    logger.info("{} 刷新共享成功", dataSourceId);
    //刷新内存数据源
    dynamicMemoryMap.put(dataSourceId, dataSource);
    logger.info("{} 刷新内存成功", dataSourceId);
    ((DruidDataSource) dataSource).init();
    logger.info("{} init初始化成功", dataSourceId);
    DynamicDataSource source = SpringUtil.getBean(DynamicDataSource.class);
    source.setTargetDataSources(dynamicMemoryMap);
    //刷新系统数据库配置
    source.afterPropertiesSet();
    logger.info("{} afterPropertiesSet 刷新成功", dataSourceId);

  }


  /**
   * 校验内存中数据库是否存在
   *
   * @param dataSourceId 数据库唯一性标识
   * @return 是否存在
   */
  @Override
  public Boolean containMemoryDataSource(String dataSourceId) {
    return dynamicMemoryMap.containsKey(dataSourceId);
  }

  /**
   * 获取所有datasource
   *
   * @return Map<Object               ,               Object>
   */
  @Override
  public Map<Object, Object> getMemoryDataSource() {
    return dynamicMemoryMap;
  }

  /**
   * 新增一个数据源
   *
   * @param dataSourceId 数据源唯一性标识
   * @param dataSource 数据源
   */
  @Override
  public void putMemoryDataSource(String dataSourceId, DataSource dataSource) {
    dynamicMemoryMap.put(dataSourceId, dataSource);
  }

  /**
   * 通过唯一性标识获取数据库主键
   *
   * @param dataSourceId dataSourceId
   * @return DataSource
   */
  @Override
  public DataSource getMemoryDataSourceById(String dataSourceId) {
    return (DataSource) dynamicMemoryMap.get(dataSourceId);
  }

  /**
   * 通过数据源信息 获取数据库唯一标识
   *
   * @param databaseDTO 数据源信息
   * @return 唯一标识
   */
  @Override
  public String getTokenByDataSource(DatabaseDTO databaseDTO) {
    return Md5Util.md5Encode(
        databaseDTO.getIp().concat(databaseDTO.getPort()).concat(databaseDTO.getDbName())
            .concat(databaseDTO.getUserName()).concat(databaseDTO.getPassword()), "UTF-8");
  }

  private DataSource databaseToDatasource(DatabaseDTO databaseDTO) {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUsername(databaseDTO.getUserName());
    dataSource.setPassword(databaseDTO.getPassword());
    dataSource.setDbType(DBTypeEnum.getDbType(databaseDTO.getDbType()));
    dataSource.setDriverClassName(DBTypeEnum.getDriverClass(databaseDTO.getDbType()));
    dataSource.setUrl(StringUtils.isEmpty(databaseDTO.getUrl()) ? DBTypeEnum
        .getUrl(databaseDTO.getDbType(), databaseDTO.getIp(), databaseDTO.getPort(),
            databaseDTO.getDbName()) : databaseDTO.getUrl());
    return dataSource;
  }

  private DatabaseDTO dataSourceToDatabase(DataSource dataSource) {
    DatabaseDTO databaseDTO = new DatabaseDTO();
    DruidDataSource druidDataSource = (DruidDataSource) dataSource;
    databaseDTO.setPassword(druidDataSource.getPassword());
    databaseDTO.setUserName(druidDataSource.getUsername());
    databaseDTO.setDbType(druidDataSource.getDbType());
    databaseDTO.setUrl(druidDataSource.getUrl());
    return databaseDTO;
  }

}
